package com.py.custom.tms.application.jvm.bytecode;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.*;

/**
 * description：
 *
 * @author pengyou
 * @version 1.0.0
 * @date 2022/2/20
 */
public class ReverseBuildCodeByte {

    public static void main(String[] args) throws IOException {
        ClassLoader classLoader = ReverseBuildCodeByte.class.getClassLoader();
        URL url = classLoader.getResource("com/py/custom/tms/application/jvm/bytecode/MyTest1.class");
        URL url2 = classLoader.getResource("com/py/custom/tms/application/jvm/bytecode/");
        if (Objects.isNull(url) || Objects.isNull(url2)) {
            throw new RuntimeException("不存在该资源文件");
        }
        String path = url.getPath();
        String outPath = url2.getPath() + "MyTest1_Re.java";

        File file = new File(path);
        File file2 = new File(outPath);
        FileOutputStream fos = new FileOutputStream(file2);

        FileInputStream fis = new FileInputStream(file);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int len = fis.read();
        while (-1 != len) {
            baos.write(len);
            len = fis.read();
        }
        byte[] bytes = baos.toByteArray();
        //关闭资源
        baos.close();
        fis.close();

        List<Byte> byteList = new ArrayList<>(bytes.length);
        for (byte aByte : bytes) {
            byteList.add(aByte);
        }

        ClassReverseBuildCode classReverse = new ClassReverseBuildCode();

        int start = 0;
        //1.读取 4个字节; magic：魔数
        List<Byte> magicByte = byteList.subList(start, start + 4);
        classReverse.setMagic(magicByte);
        start += 4;

        //2.读取 2个字节;  minor_version：次版本
        List<Byte> minorVersionByte = byteList.subList(start, start + 2);
        classReverse.setMinorVersion(minorVersionByte);
        start += 2;

        //3.读取 2个字节;  minor_version：主版本
        List<Byte> majorVersionByte = byteList.subList(start, start + 2);
        classReverse.setMajorVersion(majorVersionByte);
        start += 2;

        //4.读取 2个字节;  constant_pool_count：常量池的大小，0位置保留
        List<Byte> constantPoolCountByte = byteList.subList(start, start + 2);
        short constantPoolCount = ByteUtil.bytes2Short(constantPoolCountByte);
        classReverse.setConstantPoolCount(constantPoolCount);
        start += 2;

        //常量池
        List<AbstractConstantInfo> constantInfoList = new ArrayList<>(constantPoolCount);
        classReverse.setConstantPoolList(constantInfoList);
        constantInfoList.add(null);

        //5.解析constant_pool
        for (short i = 1; i < constantPoolCount; i++) {
            List<Byte> tagOne = byteList.subList(start, start + 1);
            start += 1;
            Byte tagByte = tagOne.get(0);
            AbstractConstantInfo constantInfo = ConstantInfoFactory.getConstantInfo(tagByte);
            start = constantInfo.processConstantInfo(start, byteList);
            constantInfoList.add(constantInfo);
        }

        //6.读取 2个字节; access_flags : 类访问标识。
        List<Byte> accessFlagsByte = byteList.subList(start, start + 2);
        start += 2;

        //7.读取 2个字节; this_class : 当前类this对应的类信息，常量池中索引的位置。
        List<Byte> thisClassByte = byteList.subList(start, start + 2);
        start += 2;

        //8.读取 2个字节; super_class : 当前类super对应的类信息，常量池中索引的位置。
        List<Byte> superClassByte = byteList.subList(start, start + 2);
        start += 2;

        //9.读取 2个字节; interfaces_count : 接口数量
        List<Byte> interfacesCountByte = byteList.subList(start, start + 2);
        start += 2;

        //10.interfaces

        //11.读取 2个字节; fields_count : 字段数量
        List<Byte> fieldCountCountByte = byteList.subList(start, start + 2);
        start += 2;
        short fieldCount = ByteUtil.bytes2Short(fieldCountCountByte);

        //12.解析field_info
        for (int i = 0; i < fieldCount; i++) {

        }

        //13.读取 2个字节; methods_count : 方法数量

        //14.解析method_info

        //15.读取 2个字节; attributes_count : 属性数量

        //16.解析attribute_info

    }
}

class ClassReverseBuildCode {

    private String magic;

    private String minorVersion;

    private String majorVersion;

    private Short constantPoolCount;

    private List<AbstractConstantInfo> constantPoolList;

    private String accessFlags;

    private String thisClass;

    private String superClass;

    private Short interfacesCount;

    private String interfaceList;

    private Short fieldsCount;

    private String fieldList;

    private Short methodsCount;

    private String methodList;

    private Short attributeCount;

    private String attributeList;

    public void setMagic(List<Byte> magicByte) {
        byte[] magicData = new byte[4];
        for (int i = 0; i < magicByte.size(); i++) {
            magicData[i] = magicByte.get(i);
        }
        this.magic = new String(magicData);
    }

    public void setMinorVersion(List<Byte> minorVersionByte) {
        short num = ByteUtil.bytes2Short(minorVersionByte);
        if (num == 0) {
            this.minorVersion = "0";
        }
    }

    public void setMajorVersion(List<Byte> majorVersionByte) {
        short num = ByteUtil.bytes2Short(majorVersionByte);
        if (num == 52) {
            this.majorVersion = "1.8";
        }
    }

    public void setConstantPoolCount(Short constantPoolCount) {
        this.constantPoolCount = constantPoolCount;
    }

    public void setConstantPoolList(List<AbstractConstantInfo> constantPoolList) {
        this.constantPoolList = constantPoolList;
    }

    public void setAccessFlags(List<Byte> accessFlagByte) {
        this.accessFlags = accessFlags;
    }

    public void setThisClass(List<Byte> thisClassByte) {
        this.thisClass = thisClass;
    }

    public void setSuperClass(List<Byte> superClassByte) {
        this.superClass = superClass;
    }

    public void setInterfacesCount(Short interfacesCount) {
        this.interfacesCount = interfacesCount;
    }

    public void setInterfaceList(String interfaceList) {
        this.interfaceList = interfaceList;
    }

    public void setFieldsCount(Short fieldsCount) {
        this.fieldsCount = fieldsCount;
    }

    public void setFieldList(String fieldList) {
        this.fieldList = fieldList;
    }

    public void setMethodsCount(Short methodsCount) {
        this.methodsCount = methodsCount;
    }

    public void setMethodList(String methodList) {
        this.methodList = methodList;
    }

    public void setAttributeCount(Short attributeCount) {
        this.attributeCount = attributeCount;
    }

    public void setAttributeList(String attributeList) {
        this.attributeList = attributeList;
    }

    public String getMagic() {
        return magic;
    }

    public String getMinorVersion() {
        return minorVersion;
    }

    public String getMajorVersion() {
        return majorVersion;
    }

    public Short getConstantPoolCount() {
        return constantPoolCount;
    }

    public List<AbstractConstantInfo> getConstantPoolList() {
        return constantPoolList;
    }

    public String getAccessFlags() {
        return accessFlags;
    }

    public String getThisClass() {
        return thisClass;
    }

    public String getSuperClass() {
        return superClass;
    }

    public Short getInterfacesCount() {
        return interfacesCount;
    }

    public String getInterfaceList() {
        return interfaceList;
    }

    public Short getFieldsCount() {
        return fieldsCount;
    }

    public String getFieldList() {
        return fieldList;
    }

    public Short getMethodsCount() {
        return methodsCount;
    }

    public String getMethodList() {
        return methodList;
    }

    public Short getAttributeCount() {
        return attributeCount;
    }

    public String getAttributeList() {
        return attributeList;
    }
}

enum TagType {
    // (tag-u1, length-u2, bytes)
    UTF8_INFO((byte) 1),

    // (tag-u1, bytes-u4)
    INTEGER_INFO((byte) 3),

    // (tag-u1, bytes-u4)
    FLOAT_INFO((byte) 4),

    // (tag-u1, bytes-u8)
    LONG_INFO((byte) 5),

    // (tag-u1, bytes-u8)
    DOUBLE_INFO((byte) 6),

    // (tag-u1, index-u2)
    CLASS_INFO((byte) 7),

    // (tag-u1, index-u2)
    STRING_INFO((byte) 8),

    // (tag-u1, index-u2, index-u2)
    FIELD_REF_INFO((byte) 9),

    // (tag-u1, index-u2, index-u2)
    METHOD_REF_INFO((byte) 10),

    // (tag-u1, index-u2, index-u2)
    INTERFACE_METHOD_REF_INFO((byte) 11),

    // (tag-u1, index-u2, index-u2)
    NAME_AND_TYPE_INFO((byte) 12),

    // (tag-u1, reference_kind-u1, reference_index-u2)
    METHOD_HANDLE_INFO((byte) 15),

    // (tag-u1, descriptor_index-u2)
    METHOD_TYPE_INFO((byte) 16),

    //  (tag-u1, bootstrap_method_attr_index-u2, name_and_type_index-u2)
    INVOKE_DYNAMIC_INFO((byte) 18),
    ;

    byte tag;

    TagType(byte tag) {
        this.tag = tag;
    }

    public static TagType getTagType(byte tag) {
        for (TagType tagType : values()) {
            if (tagType.tag == tag) {
                return tagType;
            }
        }
        throw new RuntimeException("常量类型找不到");
    }

}

class ConstantInfoFactory {

    private static Map<TagType, AbstractConstantInfo> cacheConstantInfo = new HashMap<>();

    static {
        cacheConstantInfo.put(TagType.UTF8_INFO, new Utf8Info(TagType.UTF8_INFO.tag));
        cacheConstantInfo.put(TagType.INTEGER_INFO, new IntegerInfo(TagType.INTEGER_INFO.tag));
        cacheConstantInfo.put(TagType.FLOAT_INFO, new FloatInfo(TagType.FLOAT_INFO.tag));
        cacheConstantInfo.put(TagType.LONG_INFO, new LongInfo(TagType.LONG_INFO.tag));
        cacheConstantInfo.put(TagType.DOUBLE_INFO, new DoubleInfo(TagType.DOUBLE_INFO.tag));
        cacheConstantInfo.put(TagType.CLASS_INFO, new ClassInfo(TagType.CLASS_INFO.tag));
        cacheConstantInfo.put(TagType.STRING_INFO, new StringInfo(TagType.STRING_INFO.tag));
        cacheConstantInfo.put(TagType.FIELD_REF_INFO, new FieldRefInfo(TagType.FIELD_REF_INFO.tag));
        cacheConstantInfo.put(TagType.METHOD_REF_INFO, new MethodRefInfo(TagType.METHOD_REF_INFO.tag));
        cacheConstantInfo.put(TagType.INTERFACE_METHOD_REF_INFO, new InterfaceMethodRefInfo(TagType.INTERFACE_METHOD_REF_INFO.tag));
        cacheConstantInfo.put(TagType.NAME_AND_TYPE_INFO, new NameAndTypeInfo(TagType.NAME_AND_TYPE_INFO.tag));
        cacheConstantInfo.put(TagType.METHOD_HANDLE_INFO, new MethodHandleInfo(TagType.METHOD_HANDLE_INFO.tag));
        cacheConstantInfo.put(TagType.METHOD_TYPE_INFO, new MethodTypeInfo(TagType.METHOD_TYPE_INFO.tag));
        cacheConstantInfo.put(TagType.INVOKE_DYNAMIC_INFO, new InvokeDynamicInfo(TagType.INVOKE_DYNAMIC_INFO.tag));
    }

    public static AbstractConstantInfo getConstantInfo(byte tag) {
        try {
            AbstractConstantInfo originConstantInfo = cacheConstantInfo.get(TagType.getTagType(tag));
            Class<? extends AbstractConstantInfo> clazz = originConstantInfo.getClass();
            Constructor<? extends AbstractConstantInfo> constructor = clazz.getConstructor(byte.class);
            return constructor.newInstance(tag);
        } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException ex) {
            throw new RuntimeException("创建常量信息错误");
        }
    }
}

abstract class AbstractConstantInfo {

    private byte tag;

    public AbstractConstantInfo(byte tag) {
        this.tag = tag;
    }

    protected byte getTag() {
        return tag;
    }

    /**
     * 处理常量信息
     *
     * @param start     起始读取位置
     * @param codeBytes 编译后的字节码
     * @return 常量对象
     */
    public abstract int processConstantInfo(int start, List<Byte> codeBytes);
}

class Utf8Info extends AbstractConstantInfo {

    private short length;

    private String data;

    public Utf8Info(byte tag) {
        super(tag);
    }

    /**
     * 结构：(tag-u1, length-u2, bytes)
     *
     * @param start     起始读取位置
     * @param codeBytes 编译后的字节码
     * @return 常量对象
     */
    @Override
    public int processConstantInfo(int start, List<Byte> codeBytes) {
        List<Byte> lengthByte = codeBytes.subList(start, start + 2);
        this.length = ByteUtil.bytes2Short(lengthByte);
        List<Byte> dataByte = codeBytes.subList(start + 2, start + 2 + length);
        this.data = ByteUtil.bytes2Utf8(dataByte);
        return start + 2 + length;
    }
}

class IntegerInfo extends AbstractConstantInfo {

    private int value;

    public IntegerInfo(byte tag) {
        super(tag);
    }

    /**
     * 处理常量信息
     *
     * @param start     起始读取位置
     * @param codeBytes 编译后的字节码
     * @return 常量对象
     */
    @Override
    public int processConstantInfo(int start, List<Byte> codeBytes) {
        List<Byte> dataByte = codeBytes.subList(start, start + 4);
        this.value = ByteUtil.bytes2Int(dataByte);
        return start + 4;
    }
}

class FloatInfo extends AbstractConstantInfo {

    private float value;

    public FloatInfo(byte tag) {
        super(tag);
    }

    /**
     * 处理常量信息
     *
     * @param start     起始读取位置
     * @param codeBytes 编译后的字节码
     * @return 常量对象
     */
    @Override
    public int processConstantInfo(int start, List<Byte> codeBytes) {
        List<Byte> dataByte = codeBytes.subList(start, start + 4);
        this.value = ByteUtil.bytes2Float(dataByte);
        return start + 4;
    }
}

class LongInfo extends AbstractConstantInfo {

    private long value;

    public LongInfo(byte tag) {
        super(tag);
    }

    /**
     * 处理常量信息
     *
     * @param start     起始读取位置
     * @param codeBytes 编译后的字节码
     * @return 常量对象
     */
    @Override
    public int processConstantInfo(int start, List<Byte> codeBytes) {
        List<Byte> dataByte = codeBytes.subList(start, start + 8);
        this.value = ByteUtil.bytes2Long(dataByte);
        return start + 8;
    }
}

class DoubleInfo extends AbstractConstantInfo {

    private double value;

    public DoubleInfo(byte tag) {
        super(tag);
    }

    /**
     * 处理常量信息
     *
     * @param start     起始读取位置
     * @param codeBytes 编译后的字节码
     * @return 常量对象
     */
    @Override
    public int processConstantInfo(int start, List<Byte> codeBytes) {
        List<Byte> dataByte = codeBytes.subList(start, start + 8);
        this.value = ByteUtil.bytes2Double(dataByte);
        return start + 8;
    }
}

class ClassInfo extends AbstractConstantInfo {

    private short index;

    public ClassInfo(byte tag) {
        super(tag);
    }

    /**
     * 处理常量信息
     *
     * @param start     起始读取位置
     * @param codeBytes 编译后的字节码
     * @return 常量对象
     */
    @Override
    public int processConstantInfo(int start, List<Byte> codeBytes) {
        List<Byte> dataByte = codeBytes.subList(start, start + 2);
        this.index = ByteUtil.bytes2Short(dataByte);
        return start + 2;
    }
}

class StringInfo extends AbstractConstantInfo {

    private short index;

    public StringInfo(byte tag) {
        super(tag);
    }

    /**
     * 处理常量信息
     *
     * @param start     起始读取位置
     * @param codeBytes 编译后的字节码
     * @return 常量对象
     */
    @Override
    public int processConstantInfo(int start, List<Byte> codeBytes) {
        List<Byte> dataByte = codeBytes.subList(start, start + 2);
        this.index = ByteUtil.bytes2Short(dataByte);
        return start + 2;
    }
}

class FieldRefInfo extends AbstractConstantInfo {

    private short classIndex;

    private short nameAndTypIndex;

    public FieldRefInfo(byte tag) {
        super(tag);
    }

    /**
     * 处理常量信息
     *
     * @param start     起始读取位置
     * @param codeBytes 编译后的字节码
     * @return 常量对象
     */
    @Override
    public int processConstantInfo(int start, List<Byte> codeBytes) {
        List<Byte> dataByte = codeBytes.subList(start, start + 2);
        this.classIndex = ByteUtil.bytes2Short(dataByte);
        start += 2;
        dataByte = codeBytes.subList(start, start + 2);
        this.nameAndTypIndex = ByteUtil.bytes2Short(dataByte);
        return start + 2;
    }
}

class MethodRefInfo extends AbstractConstantInfo {

    private short classIndex;

    private short nameAndTypIndex;

    public MethodRefInfo(byte tag) {
        super(tag);
    }

    /**
     * 处理常量信息
     *
     * @param start     起始读取位置
     * @param codeBytes 编译后的字节码
     * @return 常量对象
     */
    @Override
    public int processConstantInfo(int start, List<Byte> codeBytes) {
        List<Byte> dataByte = codeBytes.subList(start, start + 2);
        this.classIndex = ByteUtil.bytes2Short(dataByte);
        start += 2;
        dataByte = codeBytes.subList(start, start + 2);
        this.nameAndTypIndex = ByteUtil.bytes2Short(dataByte);
        return start + 2;
    }
}

class InterfaceMethodRefInfo extends AbstractConstantInfo {

    private short classIndex;

    private short nameAndTypIndex;

    public InterfaceMethodRefInfo(byte tag) {
        super(tag);
    }

    /**
     * 处理常量信息
     *
     * @param start     起始读取位置
     * @param codeBytes 编译后的字节码
     * @return 常量对象
     */
    @Override
    public int processConstantInfo(int start, List<Byte> codeBytes) {
        List<Byte> dataByte = codeBytes.subList(start, start + 2);
        this.classIndex = ByteUtil.bytes2Short(dataByte);
        start += 2;
        dataByte = codeBytes.subList(start, start + 2);
        this.nameAndTypIndex = ByteUtil.bytes2Short(dataByte);
        return start + 2;
    }
}

class NameAndTypeInfo extends AbstractConstantInfo {

    private short nameIndex;

    private short typeIndex;

    public NameAndTypeInfo(byte tag) {
        super(tag);
    }

    /**
     * 处理常量信息
     *
     * @param start     起始读取位置
     * @param codeBytes 编译后的字节码
     * @return 常量对象
     */
    @Override
    public int processConstantInfo(int start, List<Byte> codeBytes) {
        List<Byte> dataByte = codeBytes.subList(start, start + 2);
        this.nameIndex = ByteUtil.bytes2Short(dataByte);
        start += 2;
        dataByte = codeBytes.subList(start, start + 2);
        this.typeIndex = ByteUtil.bytes2Short(dataByte);
        return start + 2;
    }
}

class MethodHandleInfo extends AbstractConstantInfo {

    private byte referenceKind;

    private short referenceIndex;

    public MethodHandleInfo(byte tag) {
        super(tag);
    }

    /**
     * 处理常量信息
     *
     * @param start     起始读取位置
     * @param codeBytes 编译后的字节码
     * @return 常量对象
     */
    @Override
    public int processConstantInfo(int start, List<Byte> codeBytes) {
        List<Byte> dataByte = codeBytes.subList(start, start + 1);
        this.referenceKind = dataByte.get(0);
        start += 1;
        dataByte = codeBytes.subList(start, start + 2);
        this.referenceIndex = ByteUtil.bytes2Short(dataByte);
        return start + 2;
    }
}

class MethodTypeInfo extends AbstractConstantInfo {

    private short descriptorIndex;

    public MethodTypeInfo(byte tag) {
        super(tag);
    }

    /**
     * 处理常量信息
     *
     * @param start     起始读取位置
     * @param codeBytes 编译后的字节码
     * @return 常量对象
     */
    @Override
    public int processConstantInfo(int start, List<Byte> codeBytes) {
        List<Byte> dataByte = codeBytes.subList(start, start + 2);
        this.descriptorIndex = ByteUtil.bytes2Short(dataByte);
        return start + 2;
    }
}

class InvokeDynamicInfo extends AbstractConstantInfo {

    private short bootstrapMethodAttrIndex;

    private short nameAndTypeIndex;

    public InvokeDynamicInfo(byte tag) {
        super(tag);
    }

    /**
     * 处理常量信息
     *
     * @param start     起始读取位置
     * @param codeBytes 编译后的字节码
     * @return 常量对象
     */
    @Override
    public int processConstantInfo(int start, List<Byte> codeBytes) {
        List<Byte> dataByte = codeBytes.subList(start, start + 2);
        this.bootstrapMethodAttrIndex = ByteUtil.bytes2Short(dataByte);
        start += 2;
        dataByte = codeBytes.subList(start, start + 2);
        this.nameAndTypeIndex = ByteUtil.bytes2Short(dataByte);
        return start + 2;
    }
}

class ByteUtil {

    public static void main(String[] args) {
        /*
        索引：5
        01 ：tag=1 ，CONSTANT_Utf8_info
        00 01 ：1 表示1个字节。
        61 ：ascii 码，表示 a
         */
        ArrayList<Byte> objects = new ArrayList<>();
        //128+24
        objects.add((byte) (6 * 16 + 1));

        System.out.println(bytes2Utf8(objects));
    }

    public static short bytes2Short(List<Byte> lengthByte) {
        // 高位在前，低位再后
        byte[] shortBytes = new byte[2];
        for (int i = 0; i < lengthByte.size(); i++) {
            shortBytes[i] = lengthByte.get(i);
        }
        // 2个长度的字节数组 变成 short
        return (short) (((shortBytes[0] & 0x00FF) << 8) | (shortBytes[1] & 0x00FF));
    }

    public static int bytes2Int(List<Byte> intDataByte) {
        // 高位在前，低位再后
        byte[] shortBytes = new byte[4];
        for (int i = 0; i < intDataByte.size(); i++) {
            shortBytes[i] = intDataByte.get(i);
        }
        // 2个长度的字节数组 变成 short
        return (((shortBytes[0] & 0x00FF) << 8) | (shortBytes[1] & 0x00FF));
    }

    public static float bytes2Float(List<Byte> intDataByte) {
        // 高位在前，低位再后
        byte[] shortBytes = new byte[4];
        for (int i = 0; i < intDataByte.size(); i++) {
            shortBytes[i] = intDataByte.get(i);
        }
        // 2个长度的字节数组 变成 short
        return (((shortBytes[0] & 0x00FF) << 8) | (shortBytes[1] & 0x00FF));
    }

    public static long bytes2Long(List<Byte> intDataByte) {
        // 高位在前，低位再后
        byte[] shortBytes = new byte[4];
        for (int i = 0; i < intDataByte.size(); i++) {
            shortBytes[i] = intDataByte.get(i);
        }
        // 2个长度的字节数组 变成 short
        return (((shortBytes[0] & 0x00FF) << 8) | (shortBytes[1] & 0x00FF));
    }

    public static double bytes2Double(List<Byte> intDataByte) {
        // 高位在前，低位再后
        byte[] shortBytes = new byte[4];
        for (int i = 0; i < intDataByte.size(); i++) {
            shortBytes[i] = intDataByte.get(i);
        }
        // 2个长度的字节数组 变成 short
        return (((shortBytes[0] & 0x00FF) << 8) | (shortBytes[1] & 0x00FF));
    }

    public static String bytes2Utf8(List<Byte> dataByte) {
        byte[] byteArray = bytes2Array(dataByte);
        return new String(byteArray, Charset.defaultCharset());
    }

    public static byte[] bytes2Array(List<Byte> dataByte) {
        byte[] byteArray = new byte[dataByte.size()];
        for (int i = 0; i < dataByte.size(); i++) {
            byteArray[i] = dataByte.get(i);
        }
        return byteArray;
    }

}